package com.recommended.service.impl;

import com.recommended.domain.DataInfo;
import com.recommended.domain.ProfessionalInfo;
import com.recommended.domain.UniversityInfo;
import com.recommended.dto.BaseSearchCondition;
import com.recommended.dto.DataInfoSearchCondition;
import com.recommended.service.DataSearchService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author xwj
 * @date 2019/4/8
 */
@Service
public class DataSearchServiceImpl implements DataSearchService {
    /**
     * 索引
     */
    private static final String INDEX = "icsearch";
    /**
     * 类型
     */
    private static final String DATA_INFO_TYPE = "data_info";
    private static final String PROFESSIONAL_INFO_TYPE = "professional_info";
    private static final String UNIVERSITY_INFO_TYPE = "university_info";
    private static final int DEFAULT_SLOP = 2;
    public static final Long DEFAULT_MAX_SIZE = 10000L;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;


    @Override
    public List<DataInfo> search(DataInfoSearchCondition dataInfoSearchCondition) {
        List<DataInfo> dataInfoList = new ArrayList<>();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (dataInfoSearchCondition.getIds() != null && dataInfoSearchCondition.getIds().length > 0) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("id", dataInfoSearchCondition.getIds()));
        } else {
            boolQueryBuilder.must(QueryBuilders.boolQuery()
                    .must(QueryBuilders.matchPhraseQuery("data_name", dataInfoSearchCondition.getKeyword()).slop(5))
                    .should(QueryBuilders.matchPhraseQuery("professional", dataInfoSearchCondition.getKeyword()))
                    .should(QueryBuilders.matchPhraseQuery("university", dataInfoSearchCondition.getKeyword())));
        }
        FieldSortBuilder sortBuilder = SortBuilders.fieldSort("publish_time")
                .order(SortOrder.DESC);
        Pageable pageable = PageRequest.of(dataInfoSearchCondition.getPage() - 1, dataInfoSearchCondition.getLimit());
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withIndices(INDEX).withTypes(DATA_INFO_TYPE)
                .withQuery(boolQueryBuilder).withPageable(pageable).withSort(sortBuilder).build();
        elasticsearchTemplate.queryForPage(searchQuery, DataInfo.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                Long total = response.getHits().totalHits;
                if (total > DEFAULT_MAX_SIZE) {
                    total = DEFAULT_MAX_SIZE;
                }
                for (SearchHit searchHit : response.getHits()) {
                    Map<String, Object> map = searchHit.getSourceAsMap();
                    DataInfo dataInfo = new DataInfo();
                    dataInfo.setId((Integer) map.get("id"));
                    dataInfo.setDataName((String) map.get("data_name"));
                    dataInfo.setProfessional((String) map.get("professional"));
                    dataInfo.setUniversity((String) map.get("university"));
                    if(!StringUtils.isEmpty(map.get("url"))){
                        dataInfo.setUrl((String) map.get("url"));
                    }
                    if(!StringUtils.isEmpty(map.get("code"))){
                        dataInfo.setCode((String) map.get("code"));
                    }
                    dataInfoList.add(dataInfo);
                }
                return new AggregatedPageImpl<T>((List<T>) dataInfoList, PageRequest.of(dataInfoSearchCondition.getPage() - 1, dataInfoSearchCondition.getLimit()), total);
            }
        });
        return dataInfoList;
    }

    @Override
    public List<ProfessionalInfo> searchProfessional(BaseSearchCondition baseSearchCondition) {
        List<ProfessionalInfo> professionalInfoList = new ArrayList<>();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(baseSearchCondition.isFilter()) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("professional_name.raw", baseSearchCondition.getKeyword()));
        } else {
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("professional_name", baseSearchCondition.getKeyword()));
        }
        SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(INDEX).setTypes(PROFESSIONAL_INFO_TYPE).setQuery(boolQueryBuilder).get();
        for (SearchHit searchHit : response.getHits()) {
            Map<String, Object> map = searchHit.getSourceAsMap();
            ProfessionalInfo professionalInfo = new ProfessionalInfo();
            professionalInfo.setId((Integer) map.get("id"));
            professionalInfo.setProfessionalName((String) map.get("professional_name"));
            professionalInfo.setUniversityId((Integer) map.get("university_id"));
            professionalInfoList.add(professionalInfo);
        }
        return professionalInfoList;
    }

    @Override
    public List<UniversityInfo> searchUniversity(BaseSearchCondition baseSearchCondition) {
        List<UniversityInfo> universityInfoList = new ArrayList<>();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("university_name", baseSearchCondition.getKeyword()));
        SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(INDEX).setTypes(UNIVERSITY_INFO_TYPE).setQuery(boolQueryBuilder).get();
        for (SearchHit searchHit : response.getHits()) {
            Map<String, Object> map = searchHit.getSourceAsMap();
            UniversityInfo universityInfo = new UniversityInfo();
            universityInfo.setId((Integer) map.get("id"));
            universityInfo.setUniversityName((String) map.get("university_name"));
            universityInfoList.add(universityInfo);
        }
        return universityInfoList;
    }
}
